package top.maof.lightsearch.handle;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.document.Document;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.SimpleHTMLFormatter;
import top.maof.lightsearch.annotation.Key;
import top.maof.lightsearch.annotation.Property;
import top.maof.lightsearch.converter.Converter;
import top.maof.lightsearch.map.Mapping;
import top.maof.lightsearch.tag.Tag;

import java.io.StringReader;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Iterator;

/**
 * 高亮处理器
 * 对结果进行进一步的高亮处理
 * 针对索引、保存且为String类型的属性
 */
@Data
@Slf4j
public class HighlighterHandle implements Handle {

    private SimpleHTMLFormatter formatter;

    private Highlighter highlighter;

    private boolean withTag = false;

    public HighlighterHandle() {
    }

    public HighlighterHandle(String preTag, String postTag) {
        formatter = new SimpleHTMLFormatter(preTag, postTag);
        withTag = true;
    }

    @Override
    public void indexHandle(Entity entity, Index index, HandleChain handleChain) throws Exception {
        handleChain.doIndex(entity, index);
    }

    @Override
    public void searchHandle(Entity entity, Index index, HandleChain handleChain) throws Exception {
        Iterator<Field> iterator = entity.getFields().iterator();
        while (iterator.hasNext()) {
            Field field = iterator.next();
            field.setAccessible(true);
            Key key = field.getAnnotation(Key.class);
            Property property = field.getAnnotation(Property.class);
            // 需要高亮的字段必须满足如下条件：
            // 1、注解的值必须为 需存储、需索引、需高亮、无转化器
            // 2、数据类型必须是 String
            if (key != null && key.isStored() && field.getType() == String.class) {
                String fieldName = handleChain.fieldName(entity.getType().value(), key.value());
                Tag tag = null;
                try {
                    // 优先使用传入的标签
                    if (!withTag) {
                        Class<? extends Tag> tagClazz = key.tag();
                        if (tagClazz != Tag.class) {
                            tag = tagClazz.newInstance();
                            formatter = new SimpleHTMLFormatter(tag.preTag(), tag.postTag());
                        } else
                            continue;
                    }
                    highlighter = new Highlighter(formatter, new QueryScorer(index.getQuery()));
                    doHighlighter(entity, index, field, fieldName, handleChain);
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("创建索引失败，error:{}", e.getMessage());
                }

            } else if (property != null && property.isStored() && property.isIndexed()
                    && field.getType() == String.class && property.convert() == Converter.class) {

                Tag tag = null;
                try {
                    // 优先使用传入的标签
                    if (!withTag) {
                        Class<? extends Tag> tagClazz = property.tag();
                        if (tagClazz != Tag.class) {
                            tag = tagClazz.newInstance();
                            formatter = new SimpleHTMLFormatter(tag.preTag(), tag.postTag());
                        } else {
                            continue;
                        }
                    }

                    this.highlighter = new Highlighter(formatter, new QueryScorer(index.getQuery()));
                    String fieldName = handleChain.fieldName(entity.getType().value(), property.value());
                    doHighlighter(entity, index, field, fieldName, handleChain);
                } catch (Exception e) {
                    e.printStackTrace();
                    log.error("创建索引失败，error:{}", e.getMessage());
                }

            }
        }

        handleChain.doSearch(entity, index);

    }


    private void doHighlighter(Entity entity, Index index, Field field, String fieldName, HandleChain handleChain) throws Exception {

        Document document = index.getDocument();
        Mapping mapping = handleChain.getMapping(field.getType());
        if (mapping != null) {
            String indexValue = (String) mapping.indexMappingJava(Arrays.asList(document.getFields(fieldName)));
            TokenStream tokenStream = index.getAnalyzer().tokenStream(fieldName, new StringReader(indexValue));
            String content = null;
            try {
                content = highlighter.getBestFragment(tokenStream, indexValue);
                if (content != null) {
                    field.set(entity.getT(), content);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }
}
